#  config.py - configuration helper utility
#  Copyright (C) 2006-2007 Sham Chukoury <eleusis@xmms.org>
#
#  This library is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 2.1 of the License, or (at your option) any later version.
#
#  This library is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#  Lesser General Public License for more details.

import atexit
try:
	import yaml
except ImportError:
	import _yaml as yaml

class ValidationError(Exception): pass

class ConfigHandler(object):
	def __init__(self, filename, autosave=True):
		self.filename = filename
		self.configs = {}
		self.autosave = autosave
		if autosave:
			atexit.register(self.save)
	
	def load(self, failOK=True):
		try:
			f = open(self.filename, "r")
			self.configs = yaml.load(f)
			f.close()
		except IOError:
			if not failOK:
				raise
	
	def save(self):
		f = open(self.filename, "w")
		yaml.dump(self.configs, f)
		f.close()
	
	def __del__(self):
		if self.autosave:
			self.save()
	
	def __getitem__(self, k):
		if not k in self.configs:
			self.configs[k] = Config()
		return self.configs[k]
	
	def __setitem__(self, k, v):
		assert isinstance(v, Config)
		self.configs[k] = v

class Config(dict):
	def __init__(self):
		dict.__init__(self)
		self.descriptions = {}
		self.defaults = {}
		self.constraints = {}
	
	def set_description(self, k, descr):
		assert k in self
		self.descriptions[k] = descr
	
	def get_description(self, k):
		return self.descriptions[k]
	
	def set_default(self, k, v):
		self.defaults[k] = v
		if not k in self:
			self[k] = v
	
	def get_default(self, k):
		return self.defaults[k]
	
	def register(self, key, descr="", default=None, constraint=None):
		"""Register property with description and default value"""
		if constraint is not None:
			self.constraints[key] = constraint
		self.set_default(key, default)
		self.set_description(key, descr)
	
	def __check_constraint(self, k, v):
		constraint = self.constraints[k]
		if isinstance(constraint, list):
			# constraint is a list of acceptable values
			if v in constraint:
				return True
		elif isinstance(constraint, NumberRangeConstraint):
			try:
				# if it's a number, this should work
				v = v + 0
			except TypeError:
				return False
			else:
				if v >= constraint.min and v <= constraint.max:
					return True
		return False
	
	def __setitem__(self, k, v):
		if not k in self.constraints:
			dict.__setitem__(self, k, v)
		else:
			if self.__check_constraint(k, v):
				dict.__setitem__(self, k, v)
			else:
				raise ValidationError("Value '%s' is not a suitable value for '%s'" % (v, k))
	
	def __delitem__(self, k):
		if k in self.descriptions:
			del self.descriptions[k]
		if k in self.defaults:
			del self.defaults[k]
		if k in self.constraints:
			del self.defaults[k]
		dict.__delitem__(self, k)

class NumberRangeConstraint:
	def __init__(self, min, max):
		self.min = min
		self.max = max
